home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / VideoToolbox 96.06.15 / VideoToolboxSources / Assign.c < prev    next >
Text File  |  1996-05-28  |  61KB  |  2,088 lines

  1. /*
  2. Assign.c
  3.  
  4. Assign is a portable runtime C interpreter that reads and executes any text
  5. "assignment" file that contains only C assignments and comments, e.g.
  6.     viewingDistance=57.0;    // inches
  7.     
  8. See Assign.note for documentation.
  9.  
  10. (It would be nice to add a flag for PrintAssignmentsToFile() that would request
  11. a read-after-write check, ala WriteLuminanceRecord in ReadLuminanceRecord.c.)
  12.  
  13. HISTORY
  14. 7/30/91 dgp    wrote it as "ReadAssignments.c".
  15. 8/4/91    dgp added new routines and renamed the old ones. Everything
  16.             seems to work, but has not been thoroughly tested.
  17. 8/5/91    dgp    MPW C 3.2 now compiles it without error messages. The MPW C macro processor,
  18.             contrary to Standard C, finds comment symbols inside strings and finds 
  19.             preprocessor directives even when # is not the first nonblank character.
  20. 8/24/91    dgp    Made compatible with THINK C 5.0.
  21.             Changed ReadAssignment file to return an error code instead of aborting
  22.             if it can't open the file.
  23. 8/26/91    dgp    Added SetVariable() and noted, in Assign.note, that the Variable array
  24.             is terminated by an element with its "type" field set to zero. (Thanks
  25.             to Evan Relkin for pointing out the omission.)
  26. 4/1/92    dgp    Tidied up Assign.note.
  27. 4/2/92    dgp Introduced ReadAssignmentBlock() and AssignmentLineWasBlank(). 
  28.             Deleted ReadAssignments().
  29. 4/5/92    dgp    ReadAssignmentFile() now closes the file before returning.
  30. 4/17/92    dgp    Expanded the explanation of flags, as requested by Evan Relkin.
  31. 5/14/92    dgp    Expanded the explanation of stringType, "".
  32. 8/4/92    dgp    Added PrintAnAssignment(), which prints out the value of a variable,
  33.             as an assignment statement, suitable for reading by the ReadAssignment
  34.             routines.
  35. 10/24/92 dgp Eliminated double spacing that occurred after //-style comment when 
  36.             assignEchoComment was requested.
  37. 3/4/93    dgp    changed the definition of emptyString slightly so that this file could be
  38.             compiled as a code resource.
  39. 5/24/93    dgp    Assignment lines may now be continued by putting a backslash at the end of
  40.             the line.
  41.             Strings may include literal linefeeds, rather than just "\n".
  42.             Printed string assignments translate linefeeds to "\n".
  43.             Skip unknown variables unless flags&assignReportUnknown is true.
  44. 5/27/93    dgp    Added "dim" field to Variable structure, and added support for
  45.             it in all the routines.
  46.             Added floatType.
  47.             Input line length is still limited to BUFFER_SIZE (presently 512) but
  48.             strings (broken up into lines of legal length and automatically 
  49.             concatenated during reading) can be of any length that will fit
  50.             in memory.
  51. 5/28/93    dgp    Added support for hex-encoded strings.
  52.             Created new bottleneck procedure ReadLine that does all the reading.
  53. 5/31/93    dgp    assignEchoAssignments now calls PrintAnAssignment. Removed "\n" from
  54.             PrintAnAssignment. Made all Print routines return assignment count,
  55.             which is now redefined as the number of array elements or scalars
  56.             that were assigned or printed. Now parse names so that
  57.             known and unknown identifiers are treated uniformly; these new
  58.             naming rules allow only simple C identifiers joined by "." and "->".
  59. 6/1/93    dgp    introduced assignNoPrintfExit flag that causes all routines to handle
  60.             errors discreetely, simply returning an error number to the caller.
  61. 6/2/93    dgp PrintAnAssignment now prints multiple assignments per 80 character line, 
  62.             adding as many continuation lines as necessary. Quoted strings (and hex
  63.             data strings) are now broken up into 80-character lines; continuation
  64.             lines are indented four spaces. Added comment field to Variable structure
  65.             and added support for it in PrintAnAssignment. Renamed all published
  66.             enum values to begin with "assign". Renamed this file "Assign.c". Added
  67.             UnequalVariables(). CheckAVariable checks that variable name is legal.
  68. 6/5/93    dgp    Removed all Macintosh dependencies, leaving pure Standard C plus a few
  69.             C++ style comments.
  70. 6/16/93    dgp    Removed prohibition: arrays of stringType are now ok.
  71.             InitializeAVariable now calls CheckAVariable with "flags" unmodified.
  72.             CheckAVariable now checks for any illegal suffix in name.
  73.             Unknown subscripted variables are now correctly reported as "UNKNOWN" 
  74.             rather than "OUT OF BOUNDS".
  75. 6/29/93    dgp    added shortDoubleType.
  76. 7/10/93    dgp    restored Standard C compatibility by using "short double" only if
  77.             SHORT_DOUBLE_OK, since Standard C doesn't allow that type.
  78. 7/19/93    dgp    noted that assign file should be opened in text mode, not binary mode, so that
  79.             \r characters will be translated properly.
  80. 7/29/93    dhb & dgp Found and fixed the newline problem that appears in the MATLAB
  81.             environment, as documented in Assign.note.
  82. 7/31/93    dgp    Added support for multidimensional arrays, with up to ASSIGN_DIMS
  83.             dimensions. Added SetVariableArray(), changed SetVariable() and
  84.             SetVariableFirstLast(), and deleted SetAVariableFirstLast().
  85. 8/2/93    dgp    Added dynamic allocation of ptrType arrays, based on scanning the current
  86.             gulp of the assign file.
  87. 8/12/93    dgp    Renamed "Variable" to "Description". Renamed most of the routines. 
  88.             The header file Assign92.h provides for backward compatibility.
  89. 8/21/93    dhb,dgp    For compatibility with MATLAB we avoid using int arguments in stdio 
  90.             calls because when MATLAB is true we'll be using MPW's stdio
  91.             library, and its int is 4 bytes whereas the THINK C int is 2 bytes.
  92.         dhb,jms    Changed "wa" in fopen in PrintAssignmentsToFile to "a".
  93.         dhb,jms,dgp Moved the NL and NEWLINE macros into VideoToolbox.h.
  94. 9/2/93    dgp    Expanded documentation of FreePtrVariables. Fixed minor bugs
  95.             in PrintAVariable for hex encoded Ptr variables so that dimensionality
  96.             of what was written is recovered when it's read.
  97. 9/7/93    dgp    Introduced STDIO_INT.
  98. 9/7/93    dhb,jms,dgp    Added OpenCalFileWrite, OpenCalFileRead, OpenCalFileReadAndCheck,
  99.             AppendDescriptions, CopyDescriptions, AllocateDescriptions,
  100.             FreeDescriptions, NumberOfDescriptions, NullDescription, and 
  101.             IsNullDescription.
  102. 9/9/93    dhb    Added ReadAssignmentStream, to read the rest of a file stream as a single
  103.             gulp.
  104. 9/11/93    dgp    rewrote ReadAssignmentFile to just open the file and call 
  105.             ReadAssignmentStream.
  106. 9/12/93    dhb    Changed STDIO_INT to PRINTF_INT, added inclusion of VideoToolbox
  107.             when MATLAB is on.
  108. 9/15/93    dgp    Moved documentation to Assign.note.
  109. 9/16/93    dgp    Merged Assign.h into VideoToolbox.h.
  110. 3/4/94    dgp    Increased number of float and double digits produced by PrintAnAssignment 
  111.             up to that specified in float.h. This should eliminate spurious failure
  112.             of the read-back checks.
  113.             Enhanced PrintQuotedString to correctly handle strings containing the double
  114.             quote character.
  115. 3/28/94    dgp Inserted space before comments (i.e. before "/*") and now pad the inside
  116.             of the comment delimiters with option-space instead of space for more 
  117.             pleasing word wrapping when the assignment file is viewed in that way.
  118. 6/16/94    dgp    Enhanced ParsingError to use PrintQuotedString to print the offending text
  119.             so that any spurious nonprinting characters will be revealed.
  120.             ParsingError now suppresses "\n" at end of offending string.
  121.             Enhanced PrintQuotedString to accept arbitrary open and close strings, 
  122.             typically just the traditional double-quote character.
  123. 6/17/94    dgp    Modified PrintQuotedString to print option-space "\312" directly, so
  124.             that the printout will be readable.
  125. 7/6/94    dgp    Replaced maximum linelength of 80 by a compile-time constant of 76, so
  126.             that assignment files can be emailed.
  127. 7/7/94    dgp    Added CopyDescribedVars.
  128. 7/11/94 dgp Fixed bug in ReadAssignmentStream and ReadAssignmentLine that garbled the
  129.             returned error value, if there was an error, by adding to it the number of
  130.             lines read.
  131. 7/16/94 dgp Found and fixed a memory leak in ReadAssignmentLine. GetName() called
  132.             malloc for dScratch->name, which wasn't being freed. This cost about
  133.             40 bytes for every variable. I fixed that. There still seems to be a
  134.             much smaller memory leak, which I couldn't quickly locate, and which
  135.             I can live with for the time being.
  136. 9/5/94    dgp    added support for reading of Metrowerks CodeWarrior C style of printing
  137.             NANs, e.g. NAN(021)
  138. 9/10/94    dgp    the variable "error" is now always declared int. (So it's safe to print with %d.)
  139. 7/19/95 dgp made compatible with Standard C by changing // to standard C comments.
  140. 11/8/95 dgp tracked down and fixed bug in ReadAssignmentLine(), the Description was being corrupted
  141. by freeing an unallocated variable. The fix is marked by "11/8/95".
  142. 4/19/96 dgp if MATLAB then use vsprintf and printf instead of vfprintf(stdout...), which doesn't work.
  143. Changed strtoc(), replacing NEWLINE by BOTH '\n' and '\r'.
  144. 5/28/96 dgp include myfprintf routine only #if MATLAB
  145. */
  146. #include "VideoToolbox.h"
  147. #include <stdarg.h>
  148. #if MATLAB
  149.     #define fprintf myfprintf
  150.     int myfprintf(FILE *stream,const char *format,...);
  151. #endif
  152. /*
  153. #include <assert.h>    // Already included by VideoToolbox.h
  154. #include <ctype.h>    // Already included by VideoToolbox.h
  155. #include <stdio.h>    // Already included by VideoToolbox.h
  156. #include <stdlib.h>    // Already included by VideoToolbox.h
  157. #include <string.h>    // Already included by VideoToolbox.h
  158. */
  159. #define MAX_LINELENGTH 76
  160. /*
  161. IsNan and IsFinite--see VideoToolbox.h--should work on most Intel and Motorola
  162. processors. If IsNan and IsFinite don't work on your computer, don't fret. They
  163. are used solely by UnequalDescribedVarPair. A quick fix is to use the following
  164. definitions, the only bad effect of which will be that comparisons of NANs may
  165. appear spuriously unequal.
  166. */
  167. #ifndef IsFinite
  168.     #define IsNan(x) 0
  169.     #define IsFinite(x) 1
  170. #endif
  171. Description SetAVariable(short type,void *ptr,char *name,unsigned long dim
  172.     ,const char *comment);    /* old style */
  173.  
  174. /* the following are private, not intended for use outside this file */
  175. #define IsIntegral(type) ((type)>=charType&&(type)<=unsignedLongType || (type)>=charPtrType&&(type)<=unsignedLongPtrType)
  176. #define IsFloating(type) ((type)>=floatType&&(type)<=doubleType || (type)>=floatPtrType&&(type)<=doublePtrType)
  177. #define IsString(type) (type==stringType)
  178. #define IsPtr(type) ((type)>=charPtrType&&(type)<=doublePtrType)
  179. long CheckADescription(Description *d,const char *here,short flags);
  180. long CheckDescriptions(Description d[],const char *here,short flags);
  181. void FreeAPtrDescription(Description *d,short flags);
  182. void FreeAStringDescription(Description *d,short flags);
  183. char *GetName(char **sPtr,short flags);
  184. char *GetQuotedString(FILE *stream,char *lineBuffer,char *s,char **sPtr,short flags);
  185. long HexAssignment(FILE *stream,char *lineBuffer,char **sPtr,Description *d
  186.     ,int subscriptDims,long index,long *bytesPtr,short flags);
  187. char *NextToken(FILE *stream,char *lineBuffer,char *s,short flags);
  188. char *NextTokenInLine(FILE *stream,char *lineBuffer,char *s,short flags);
  189. char *NextTokenInThisOrNextLine(FILE *stream,char *lineBuffer,char *s,short flags);
  190. int ParseName(char **sPtr);
  191. void ParsingError(char *s,char *format,...);
  192. long PrintAnAssignmentOfElement(FILE *stream,Description *d,long index,short flags);
  193. void PrintQuotedString(FILE *stream,short *lineLengthPtr,const char *s
  194.     ,const char *openQuote,const char *closeQuote);
  195. char *ReadLine(char *lineBuffer,size_t bufferSize,FILE *stream,short flags);
  196. double strtodN(char *s,char **sPtr);
  197. long strtolN(char *s,char **sPtr,short flags);
  198. unsigned long strtoulN(char *s,char **sPtr,short flags);
  199. char strtoc(char *s,char **sPtr);
  200. #define streq2(s1,s2) (strncmp(s1,s2,strlen(s2))==0)
  201. #define streq(s1,s2) (strcmp(s1,s2)==0)
  202. long Dims(Description *d);
  203. char *ElementName(char *s,Description *d,long index);
  204. void *ElementPtr(Description *d,long index);
  205. long Elements(Description *d);
  206. void ElementSubscript(Description *d,long index,long sub[ASSIGN_DIMS]);
  207. long ElementIndex(Description *d,long sub[ASSIGN_DIMS]);
  208. int GetDimensions(Description *d,int subscriptDims,long subscript[ASSIGN_DIMS],short flags);
  209. int AllocateDescribedPtrVars(Description description[],const char *here,short flags);
  210. #ifndef TRUE
  211.     #define FALSE    0
  212.     #define TRUE    1
  213. #endif
  214. #define TOLERANCE    1e-6    /* fractional difference between two "equal" floats */
  215. #define BUFFER_SIZE 512
  216. #define ECHO_ASSIGNMENTS    (flags&assignEchoAssignments && !(flags&assignScan))
  217. #define ECHO_COMMENTS        (flags&assignEchoComments && !(flags&assignScan))
  218. #define ECHO_FILE            (flags&assignEchoFile && !(flags&assignScan))
  219. #define PRINTF_EXIT            !(flags&assignNoPrintfExit)
  220.  
  221. static int lineWasBlank=1;    /* used in ReadAssignmentLine(),    */
  222.             /* NextTokenInThisOrNextLine(), and AssignmentLineWasBlank().*/
  223. short typeSize[]={0
  224.     ,sizeof(char),sizeof(unsigned char)
  225.     ,sizeof(short),sizeof(unsigned short)
  226.     ,sizeof(long),sizeof(unsigned long)
  227.     ,sizeof(float)
  228.     #if SHORT_DOUBLE_OK
  229.         ,sizeof(short double)
  230.     #endif
  231.     ,sizeof(double)
  232.     ,sizeof(char),sizeof(unsigned char)
  233.     ,sizeof(short),sizeof(unsigned short)
  234.     ,sizeof(long),sizeof(unsigned long)
  235.     ,sizeof(float)
  236.     #if SHORT_DOUBLE_OK
  237.         ,sizeof(short double)
  238.     #endif
  239.     ,sizeof(double)
  240.     ,sizeof(char *)
  241.     ,0
  242. };
  243. char typeName[][20]={"none"
  244.     ,"char","unsigned char","short","unsigned short"
  245.     ,"long","unsigned long","float"
  246.     #if SHORT_DOUBLE_OK
  247.         ,"short double"
  248.     #endif
  249.     ,"double"
  250.     ,"char *","unsigned char *","short *","unsigned short *"
  251.     ,"long *","unsigned long *","float *"
  252.     #if SHORT_DOUBLE_OK
  253.         ,"short double *"
  254.     #endif
  255.     ,"double *"
  256.     ,"string"
  257.     ,"unknown"
  258. };
  259. enum{assignNeedMore=0x4000,assignAlreadyChecked=0x2000
  260.     ,assignScan=0x100,assignLocalCall=0x200
  261. };/*flags used only in this file*/
  262.  
  263. /*
  264. ROUTINE: OpenCalFileReadAndCheck
  265. PURPOSE:
  266.     Open up a calibration file for reading.
  267.     
  268.     If the file exists in the current directory, it is used.
  269.     If not, then if it exists in the preferences folder, it used.
  270. */
  271.  
  272. FILE *OpenCalFileReadAndCheck(char *filename)
  273. {
  274.     FILE *stream;
  275.     
  276.     stream = OpenCalFileRead(filename);
  277.     if (stream == NULL)
  278.         PrintfExit("OpenCalFileReadAndCheck: cannot open file \"%s\"",filename);
  279.     return(stream);    
  280. }
  281.  
  282. /*
  283. ROUTINE: OpenCalFileRead
  284. PURPOSE:
  285.     Open up a calibration file for reading.  Don't abort on error.
  286.     
  287.     If the file exists in the current directory, it is used.
  288.     If not, then if it exists in the preferences folder, it used.
  289. */
  290.  
  291. FILE *OpenCalFileRead(char *filename)
  292. {
  293.     FILE *stream;
  294.     
  295.     /* Try the current directory */
  296.     stream = fopen(filename,"r");
  297.     if (stream != NULL) return(stream);
  298.     
  299.     /* Try the preferences folder */
  300.     OpenPreferencesFolder();
  301.     stream = fopen(filename,"r");
  302.     ClosePreferencesFolder();
  303.     return(stream);    
  304. }
  305.  
  306. /*
  307. ROUTINE: OpenCalFileWrite
  308. PURPOSE:
  309.     Open up a calibration file for appending.
  310.     
  311.     If the file exists in the current directory, it is used.
  312.     If not, then if it exists in the Preferences folder, it used.
  313.     If not, the file is created in the Preferences folder.
  314. */
  315.  
  316. FILE *OpenCalFileWrite(char *filename)
  317. {
  318.     FILE *stream;
  319.     
  320.     /* Try in the current directory */
  321.     stream = fopen(filename,"r");
  322.     if (stream != NULL) {
  323.         fclose(stream);
  324.         stream = fopen(filename,"a");
  325.         if (stream == NULL)
  326.             PrintfExit("OpenCalFileWrite: cannot reopen file \"%s\"",filename);
  327.         return(stream);
  328.     }
  329.     
  330.     /* Try the Preferences folder */
  331.     OpenPreferencesFolder();
  332.     stream = fopen(filename,"r");
  333.     ClosePreferencesFolder();
  334.     if (stream != NULL) {
  335.         fclose(stream);
  336.         OpenPreferencesFolder();
  337.         stream = fopen(filename,"a");
  338.         ClosePreferencesFolder();
  339.         if (stream == NULL)
  340.             PrintfExit("OpenCalFileWrite: cannot reopen file \"%s\" in Preferences folder"
  341.                 ,filename);
  342.         return(stream);
  343.     }
  344.     
  345.     /* Create it in the Preferences folder */
  346.     OpenPreferencesFolder();
  347.     stream = fopen(filename,"a");
  348.     ClosePreferencesFolder();
  349.     if (stream == NULL)
  350.         PrintfExit("OpenCalFileWrite: cannot create file \"%s\" in Preferences folder.",filename);
  351.     return(stream);
  352. }
  353.  
  354. /*
  355. ROUTINE: AppendDescriptions
  356. PURPOSE:
  357.     Appends the second descriptions array onto the end of the first,
  358.     which is reallocated with more space.
  359.     
  360.     The source array is not freed; the caller should do that.
  361. */
  362. void AppendDescriptions(Description **d,Description *s)
  363. {
  364.     Description *dTemp;
  365.     long n1, n2, n;
  366.     
  367.     n1 = NumberOfDescriptions(*d);
  368.     n2 = NumberOfDescriptions(s);
  369.     n = n1+n2;
  370.     dTemp = AllocateDescriptions(n);
  371.     CopyDescriptions(dTemp,*d);
  372.     CopyDescriptions(dTemp+n1,s);
  373.     FreeDescriptions(*d);
  374.     *d=dTemp;
  375. }
  376.  
  377. /*
  378. ROUTINE: CopyDescriptions
  379. PURPOSE:
  380.     Copy one null-terminated array of descriptions to another, which is assumed to
  381.     be big enough.
  382. */
  383. void CopyDescriptions(Description *d,Description *s)
  384. {
  385.     long i = 0;
  386.     
  387.     while ( !IsNullDescription(s[i]) ) {
  388.         d[i]=s[i];
  389.         i++;
  390.     }
  391.     d[i]=NullDescription();
  392. }
  393.  
  394. /*
  395. ROUTINE: AllocateDescriptions
  396. PURPOSE:
  397.     Allocate space for variable descriptions.
  398.     Adds one to the passed size to allow use of null element as end marker.
  399.     Nulls all the elements.
  400. */
  401. Description *AllocateDescriptions(long n)
  402. {
  403.     Description *d;
  404.  
  405.     d = calloc(n+1,sizeof(Description));
  406.     if(d == NULL)PrintfExit("AllocateDescription: memory allocation failure.");
  407.     d[0]=NullDescription();
  408.     for(;n>0;n--)d[n]=d[0];
  409.     return(d);
  410. }
  411.  
  412.  
  413. /*
  414. ROUTINE: FreeDescriptions
  415. PURPOSE:
  416.     Free the description space.  Does not affect the described variables
  417.     themselves.
  418. */
  419. void FreeDescriptions(Description *d)
  420. {
  421.     free(d);    
  422. }
  423.  
  424. /*
  425. ROUTINE: NumberOfDescriptions
  426. PURPOSE:
  427.     Find the size of an array of descriptions.
  428.     
  429.     Does not count the trailing null description.
  430. */
  431. long NumberOfDescriptions(Description *d)
  432. {
  433.     long i=0;
  434.     
  435.     while ( !IsNullDescription(d[i]) ) i++;
  436.     return(i);
  437. }
  438.  
  439. /*
  440. ROUTINE: NullDescription
  441. PURPOSE:
  442.     Returns a null description.
  443.     Standard C specifies that static variables are initialized to zero.
  444. */
  445. Description NullDescription(void)
  446. {
  447.     static Description d;
  448.     return d;
  449. }
  450.  
  451. Description Describe(short type,void *ptr,char *name,const char *comment)
  452. {
  453.     static Description var;
  454.  
  455.     var=DescribeArray(type,ptr,name,comment,0L);
  456.     if(IsPtr(type)) var.sizedOnce=var.sized=0;
  457.     return var;
  458. }
  459.  
  460. Description DescribeArray(short type,void *ptr,char *name,const char *comment,...)
  461. /* WARNING: the dimensions must be (long) and the last argument must be 0L. */
  462. {
  463.     va_list args;
  464.     static Description var;
  465.     int i;
  466.     long dim;
  467.     
  468.     var.name=name;
  469.     var.ptr=ptr;
  470.     var.type=type;
  471.     var.firstElement=0;
  472.     for(i=0;i<ASSIGN_DIMS;i++)var.dim[i]=0;
  473.     var.comment=comment;
  474.     va_start(args,comment);
  475.     for(i=0;;i++){
  476.         dim=va_arg(args,long);
  477.         if(dim==0)break;
  478.         if(i>=ASSIGN_DIMS)PrintfExit("Describe/Array/FirstLast: "
  479.             "too many dimensions (or missing final 0L) for \"%s\".\n",var.name);
  480.         var.dim[i]=dim;
  481.     }
  482.     va_end(args);
  483.     if(var.type!=0 && var.ptr==NULL)
  484.         PrintfExit("Describe/Array/FirstLast: \"%s\" ptr is NULL.\n",var.name);
  485.     /* Note: we're clearing the malloced flag, which is the only safe assumption. */
  486.     var.malloced=0;
  487.     var.sizedOnce=var.sized=1;
  488.     return var;
  489. }
  490. Description DescribeFirstLast(short type,void *ptr,char *name
  491.     ,const char *comment,long firstElement,long lastElement)
  492. {
  493.     static Description var;
  494.     long dim;
  495.     
  496.     dim=1+lastElement-firstElement;
  497.     var=DescribeArray(type,ptr,name,comment,dim,0L);
  498.     var.firstElement=firstElement;
  499.     return var;
  500. }
  501. Description SetAVariable(short type,void *ptr,char *name,unsigned long dim
  502.     ,const char *comment)
  503. /* Old style, retained solely for compatibility */
  504. {
  505.     if(dim==0)return Describe(type,ptr,name,comment);
  506.     else return DescribeArray(type,ptr,name,comment,dim,0L);
  507. }
  508. int AllocateDescribedPtrVars(Description description[],const char *here,short flags)
  509. {
  510.     register Description *d;
  511.     
  512.     for(d=description;d->type!=0;d++){
  513.         if(IsPtr(d->type) && d->sizedOnce && !d->malloced){
  514.             assert(d->ptr!=NULL);
  515.             *(void **)d->ptr=malloc(Elements(d)*typeSize[d->type]);
  516.             if(*(void **)d->ptr==NULL){
  517.                 if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes for \"%s\".\n"
  518.                     ,here,Elements(d)*typeSize[d->type],d->name);
  519.                 else return assignMemoryError;
  520.             }
  521.             d->sized=d->malloced=1;
  522.             InitializeADescribedVar(d,flags);
  523.         }
  524.     }
  525.     return 0;
  526. }
  527.  
  528. long CheckDescriptions(Description d[],const char *here,short flags)
  529. {
  530.     int error;
  531.     
  532.     if(flags&assignAlreadyChecked)return 0;
  533.     for(;d->type!=0;d++){
  534.         error=CheckADescription(d,here,flags);
  535.         if(error)return error;
  536.     }
  537.     return 0;
  538. }
  539.  
  540. long CheckADescription(Description *d,const char *here,short flags)
  541. {
  542.     char *s,*sOk;
  543.     
  544.     if(flags&assignAlreadyChecked)return 0;
  545.     if(d->type<=0 || d->type>=unknownType){
  546.         if(PRINTF_EXIT)PrintfExit("%s: \"%s\" has unknown type %ld.\n"
  547.             ,here,d->name,(long)d->type);
  548.         else return assignVariableError;
  549.     }
  550.     if(!IsPtr(d->type) && d->ptr==NULL){
  551.         if(PRINTF_EXIT)PrintfExit("%s: \"%s\" ptr is NULL.\n"
  552.             ,here,d->name);
  553.         else return assignVariableError;
  554.     }
  555.     /* Squeeze any space out of the name */
  556.     for(s=sOk=d->name;*s!=0;s++)if(!isspace(*s))*sOk++=*s;
  557.     *sOk=0;
  558.     /* Check name's syntax */
  559.     s=d->name;
  560.     if(ParseName(&s) || *s!=0){
  561.         if(PRINTF_EXIT)PrintfExit("%s: illegal name \"%s\".\n"
  562.             ,here,d->name);
  563.         else return assignVariableError;
  564.     }
  565.     return 0;
  566. }
  567.  
  568. int UnequalDescribedVars(Description d1[],Description d2[],short flags)
  569. /*
  570. Compares the data pointed to by the two Description arrays and returns
  571. true if the structs are legal and the data are equal, false otherwise.
  572. */
  573. {
  574.     int error;
  575.     
  576.     for(;d1->type!=0 && d2->type!=0;d1++,d2++){
  577.         error=UnequalDescribedVarPair(d1,d2,flags);
  578.         if(error)return error;
  579.     }
  580.     return 0;
  581. }
  582.  
  583. int CopyDescribedVars(Description d1[],Description d2[],short flags)
  584. /*
  585. Copies the data pointed to by the first Description array, overwriting the data
  586. pointed to by the second. Returns 0 if successful, i.e. the structs are legal and 
  587. the types and names are consistent, nonzero otherwise.
  588. */
  589. {
  590.     int error;
  591.     
  592.     for(;d1->type!=0 && d2->type!=0;d1++,d2++){
  593.         error=CopyDescribedVarPair(d1,d2,flags);
  594.         if(error)return error;
  595.     }
  596.     return 0;
  597. }
  598.  
  599. long Elements(Description *d)
  600. {
  601.     long elements=1;
  602.     int i;
  603.  
  604.     for(i=0;i<ASSIGN_DIMS && d->dim[i]>0;i++)elements*=d->dim[i];
  605.     return elements;
  606. }
  607. long Dims(Description *d)
  608. {
  609.     int i;
  610.  
  611.     for(i=0;i<ASSIGN_DIMS && d->dim[i]>0;i++) ;
  612.     return i;
  613. }
  614. void *ElementPtr(Description *d,long index)
  615. /* Returns NULL if PtrType array not allocated. */
  616. {
  617.     char *ptr;
  618.     
  619.     if(IsPtr(d->type)){
  620.         assert(d->ptr!=NULL);
  621.         ptr=*(void **)d->ptr;
  622.     }else ptr=d->ptr;
  623.     if(ptr!=NULL && d->dim[0]>0){
  624.         index+=d->firstElement;
  625.         ptr+=index*typeSize[d->type];
  626.     }
  627.     return ptr;
  628. }
  629. char *ElementName(char *s,Description *d,long index)
  630. {
  631.     long sub[ASSIGN_DIMS];
  632.     int i;
  633.     
  634.     ElementSubscript(d,index,sub);
  635.     sprintf(s,"%s",d->name);
  636.     for(i=0;i<Dims(d);i++)sprintf(s,"%s[%ld]",s,sub[i]);
  637.     return s;
  638. }
  639. char *DescriptionNameDimensions(Description *d);
  640. char *DescriptionNameDimensions(Description *d)
  641. {
  642.     int i;
  643.     static char s[64];
  644.     
  645.     sprintf(s,"%s",d->name);
  646.     for(i=0;d->dim[i]>0;i++)sprintf(s,"%s[%ld]",s,d->dim[i]);
  647.     assert(strlen(s)<sizeof(s));
  648.     return s;
  649. }
  650. void ElementSubscript(Description *d,long index,long sub[ASSIGN_DIMS])
  651. {
  652.     int i;
  653.  
  654.     for(i=0;i<ASSIGN_DIMS;i++)sub[i]=0;
  655.     if(Dims(d)){
  656.         for(i=Dims(d)-1;i>=0;i--){
  657.             sub[i]=index%d->dim[i];
  658.             index/=d->dim[i];
  659.         }
  660.         sub[Dims(d)-1]+=d->firstElement;
  661.     }
  662. }
  663. long ElementIndex(Description *d,long sub[ASSIGN_DIMS])
  664. {
  665.     int i;
  666.     long index=0;
  667.  
  668.     if(Dims(d)){
  669.     sub[Dims(d)-1]-=d->firstElement;
  670.     for(i=0;i<Dims(d);i++){
  671.         index*=d->dim[i];
  672.         index+=sub[i];
  673.     }
  674.     sub[Dims(d)-1]+=d->firstElement;
  675.     }
  676.     return index;
  677. }
  678. int GetDimensions(Description *d,int subscriptDims,long subscript[ASSIGN_DIMS],short flags)
  679. {
  680.     short i;
  681.     
  682.     flags;    /* dgp: prevent "unused argument" warning */
  683.     if(d->sizedOnce && Dims(d)!=subscriptDims)
  684.         return assignInconsistentDimensionsError;
  685.     if(!d->sized){
  686.         for(i=0;i<subscriptDims;i++)
  687.             if(d->dim[i]<subscript[i]+1)d->dim[i]=subscript[i]+1;
  688.         d->sizedOnce=1;
  689.     }
  690.     return 0;
  691. }
  692.  
  693. int UnequalDescribedVarPair(Description *d1,Description *d2,short flags)
  694. /*
  695. Compares the data pointed to by the two Description structs and returns true if the
  696. structs are legal and the data are equal, false otherwise. Comparison of floats
  697. and doubles allows a tolerance of +/- one part in a million--because converting
  698. to and from decimal may lose some precision--and ignores any NANs' indices (NAN04
  699. vs NANFF)--because they're not preserved.
  700. */
  701. {
  702.     unsigned char *p1,*p2;
  703.     long i,size,elements;
  704.     int error;
  705.     double a,b,e;
  706.     static const char here[]="UnequalDescribedVarPair";
  707.     char name[32+16*ASSIGN_DIMS];
  708.     
  709.     /* Squeeze space out of names */
  710.     error=CheckADescription(d1,here,flags);
  711.     if(error<0)return error;
  712.     error=CheckADescription(d2,here,flags);
  713.     if(error<0)return error;
  714.     if(d1->type!=d2->type
  715.         || !streq(d1->name,d2->name) 
  716.         || d1->firstElement!=d2->firstElement
  717.         || !streq(d1->comment,d2->comment))
  718.             {error=assignInconsistentDescriptionsError;goto done;}
  719.     for(i=0;i<ASSIGN_DIMS;i++)if(d1->dim[i]!=d2->dim[i])
  720.         {error=assignInconsistentDescriptionsError;goto done;}
  721.     p1=ElementPtr(d1,0);
  722.     p2=ElementPtr(d2,0);
  723.     size=typeSize[d1->type];
  724.     elements=Elements(d1);
  725.     if(!IsString(d1->type)){
  726.         if(memcmp(p1,p2,size*elements)!=0){
  727.             if(!IsFloating(d1->type)){error=assignUnequalDataError;goto done;}
  728.             for(i=0;i<elements;i++){
  729.                 switch(d1->type){
  730.                 case floatType:
  731.                     a=*(float *)p1;
  732.                     b=*(float *)p2;
  733.                     break;
  734.                 #if SHORT_DOUBLE_OK
  735.                     case shortDoubleType:
  736.                         a=*(short double *)p1;
  737.                         b=*(short double *)p2;
  738.                         break;
  739.                 #endif
  740.                 case doubleType:
  741.                     a=*(double *)p1;
  742.                     b=*(double *)p2;
  743.                     break;
  744.                 }
  745.                 /*  compare doubles */
  746.                 /*  ignore NaN type, since it's not preserved */
  747.                 if(IsNan(a)==0 || IsNan(b)==0){
  748.                     if(!IsFinite(a) || !IsFinite(b))
  749.                         {error=assignUnequalDataError;goto done;}
  750.                     e=a/b-1.0;
  751.                     if(e>TOLERANCE || e<-TOLERANCE)
  752.                         {error=assignUnequalDataError;goto done;}
  753.                 }
  754.                 p1+=size;
  755.                 p2+=size;
  756.             }
  757.         }
  758.     }else for(i=0;i<elements;i++){                            /* compare strings */
  759.         if(*(char **)p1!=*(char **)p2){
  760.             if((*(char **)p1==NULL) || (*(char **)p2==NULL)
  761.                 || !streq(*(char **)p1,*(char **)p2))
  762.                     {error=assignUnequalDataError;goto done;}
  763.         }
  764.         p1+=size;
  765.         p2+=size;
  766.     }
  767.     return 0;
  768. done:
  769.     if(!(flags&assignNoPrintfExit))switch(error){
  770.     case assignInconsistentDescriptionsError:
  771.         PrintfExit("%s: the two Descriptions of \"%s\" are inconsistent.\n"
  772.             ,here,d1->name);
  773.         break;
  774.     case assignUnequalDataError:
  775.         PrintfExit("%s: the two instances of \"%s\" have significantly different values.\n"
  776.             ,here,ElementName(name,d1,i));
  777.         break;
  778.     }
  779.     return error;
  780. }
  781.  
  782. int CopyDescribedVarPair(Description *d1,Description *d2,short flags)
  783. /*
  784. Copies the data pointed to by the first Description struct, overwriting the
  785. data pointed to by the second struct. Returns 0 if successful, i.e. the structs 
  786. are legal and the types and names are consistent, nonzero otherwise. 
  787. */
  788. {
  789.     unsigned char *p1,*p2;
  790.     long i,size,elements;
  791.     int error;
  792.     static const char here[]="CopyDescribedVarPair";
  793.     
  794.     /* Squeeze space out of names */
  795.     error=CheckADescription(d1,here,flags);
  796.     if(error<0)return error;
  797.     error=CheckADescription(d2,here,flags);
  798.     if(error<0)return error;
  799.     if(d1->type!=d2->type
  800.         || !streq(d1->name,d2->name) 
  801.         || d1->firstElement!=d2->firstElement)
  802.             {error=assignInconsistentDescriptionsError;goto done;}
  803.     for(i=0;i<ASSIGN_DIMS;i++)if(d1->dim[i]!=d2->dim[i])
  804.         {error=assignInconsistentDescriptionsError;goto done;}
  805.     p1=ElementPtr(d1,0);
  806.     p2=ElementPtr(d2,0);
  807.     size=typeSize[d1->type];
  808.     elements=Elements(d1);
  809.     if(!IsString(d1->type))memcpy(p2,p1,size*elements);
  810.     else{
  811.         for(i=0;i<elements;i++){                            /* copy strings */
  812.             if(!d2->malloced)*(char **)p2=NULL;
  813.             *(char **)p2=realloc(*(char **)p2,1+strlen(*(char **)p1));
  814.             if(*(void **)p2==NULL){
  815.                 if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes for \"%s\".\n"
  816.                     ,here,1+strlen(*(char **)p1),d2->name);
  817.                 else return assignMemoryError;
  818.             }
  819.             strcpy(*(char **)p2,*(char **)p1);
  820.             p1+=size;
  821.             p2+=size;
  822.         }
  823.         d2->malloced=1;
  824.     }
  825.     return 0;
  826. done:
  827.     if(!(flags&assignNoPrintfExit))switch(error){
  828.     case assignInconsistentDescriptionsError:
  829.         PrintfExit("%s: the two Descriptions of \"%s\" are inconsistent.\n"
  830.             ,here,d1->name);
  831.         break;
  832.     }
  833.     return error;
  834. }
  835.  
  836. void FreeAPtrDescription(Description *d,short flags)
  837. {
  838.     int i;
  839.     
  840.     flags;    /* dgp: prevent "unused argument" warning */
  841.     if(IsPtr(d->type)){
  842.         if(d->malloced)free(*(void **)d->ptr);
  843.         *(void **)d->ptr=NULL;
  844.         for(i=0;i<ASSIGN_DIMS;i++)d->dim[i]=0;
  845.         d->sizedOnce=d->sized=d->malloced=0;
  846.     }
  847. }
  848. void FreeAStringDescription(Description *d,short flags)
  849. {
  850.     static const char emptyString[]="";
  851.     
  852.     flags;    /* dgp: prevent "unused argument" warning */
  853.     if(IsString(d->type)){
  854.         if(d->malloced && !d->dim[0])free(*(char **)d->ptr);
  855.         *(const char **)d->ptr=emptyString;
  856.         d->malloced=0;
  857.     }
  858. }
  859. void FreeDescribedPtrVars(Description d[],short flags)
  860. {
  861.     for(;d->type!=0;d++)FreeAPtrDescription(d,flags);
  862. }
  863. void FreeADescribedVar(Description *d,short flags)
  864. {
  865.     FreeAPtrDescription(d,flags);
  866.     FreeAStringDescription(d,flags);
  867. }
  868. void FreeDescribedVars(Description d[],short flags)
  869. {
  870.     for(;d->type!=0;d++)FreeADescribedVar(d,flags);
  871. }
  872. void KeepDescribedVars(Description d[],short flags)
  873. {
  874.     flags;    /* dgp: prevent "unused argument" warning */
  875.     for(;d->type!=0;d++)d->malloced=0;
  876. }
  877. void KeepADescribedVar(Description *d,short flags)
  878. {
  879.     flags;    /* dgp: prevent "unused argument" warning */
  880.     d->malloced=0;
  881. }
  882.  
  883. long FindDescription(Description d[],void *ptr,short flags)
  884. {
  885.     long i;
  886.     
  887.     for(i=0;d[i].type!=0;i++)if(ptr==d[i].ptr)return i;
  888.     if(PRINTF_EXIT)PrintfExit("FindDescription: couldn't find your variable.\n");
  889.     return assignCouldntFindDescription;
  890. }
  891. long FindDescribedDim(Description d[],void *ptr,int i,short flags)
  892. {
  893.     long n;
  894.     
  895.     n=FindDescription(d,ptr,flags);
  896.     if(n<0)return n;
  897.     if(i<0 || i>=ASSIGN_DIMS)return 0;
  898.     return d[n].dim[i];
  899. }
  900.  
  901. long InitializeDescribedVars(Description d[],short flags)
  902. {
  903.     long j,n=0;
  904.     
  905.     for(;d->type!=0;d++){
  906.         j=InitializeADescribedVar(d,flags);
  907.         if(j<0)return j;
  908.         else n+=j;
  909.     }
  910.     return n;
  911. }
  912. long InitializeADescribedVar(Description *d,short flags)
  913. {
  914.     static const char here[]="InitializeADescribedVar";
  915.     int error;
  916.     
  917.     error=CheckADescription(d,here,flags);
  918.     if(error)return error;
  919.     if(IsPtr(d->type) && !d->malloced) *(void **)d->ptr=NULL;
  920.     if(IsPtr(d->type) || d->dim[0]){
  921.         Description w;
  922.         long i,j,n=0,elements;
  923.         
  924.         if(ElementPtr(d,0)==NULL)return n;
  925.         w=*d;
  926.         if(IsPtr(w.type))w.type+=charType-charPtrType;
  927.         for(i=0;i<ASSIGN_DIMS;i++)w.dim[i]=0;
  928.         w.sizedOnce=w.sized=1;
  929.         if(IsString(w.type))w.malloced=0;
  930.         w.firstElement=0;
  931.         elements=Elements(d);
  932.         for(i=0;i<elements;i++){
  933.             w.ptr=ElementPtr(d,i);
  934.             j=InitializeADescribedVar(&w,flags);
  935.             if(j<0)return j;
  936.             n+=j;
  937.         }
  938.         return n;
  939.     }
  940.     switch(d->type){
  941.     case charType:
  942.     case unsignedCharType:
  943.         *(char *)d->ptr=0;
  944.         break;
  945.     case shortType:
  946.     case unsignedShortType:
  947.         *(short *)d->ptr=0;
  948.         break;
  949.     case longType:
  950.     case unsignedLongType:
  951.         *(long *)d->ptr=0;
  952.         break;
  953.     case floatType:
  954.         *(float *)d->ptr=NAN;
  955.         break;
  956.     #if SHORT_DOUBLE_OK
  957.         case shortDoubleType:
  958.             *(short double *)d->ptr=NAN;
  959.             break;
  960.     #endif
  961.     case doubleType:
  962.         *(double *)d->ptr=NAN;
  963.         break;
  964.     case stringType:
  965.         FreeAStringDescription(d,flags);
  966.         break;
  967.     default:
  968.         /*  it shouldn't be possible to arrive here */
  969.         return assignVariableError;
  970.     }
  971.     return 1;
  972. }
  973.  
  974. long PrintAssignmentsToFile(const char *filename,Description d[],short flags)
  975. {
  976.     long n=0;
  977.     FILE *stream;
  978.     
  979.     stream=fopen(filename,"a");
  980.     if(stream==NULL)return assignFileError;
  981.     n=PrintAssignments(stream,d,flags);
  982.     fprintf(stream,NL);    /*  Add blank line to separate blocks */
  983.     fclose(stream);
  984.     return n;
  985. }
  986.  
  987. long PrintAssignments(FILE *stream,Description d[],short flags)
  988. {
  989.     Description *vStart=d;
  990.     long n=0,i;
  991.     
  992.     while(d->type!=0){
  993.         i=PrintAnAssignment(stream,d,flags);
  994.         if(i>0)fprintf(stream,NL);    /* new line */
  995.         if(i<0)return i;
  996.         n+=i;
  997.         d++;
  998.     }
  999.     return n;
  1000. }
  1001.  
  1002. long PrintAnAssignmentOfElement(FILE *stream,Description *d,long index,short flags)
  1003. {
  1004.     Description w;
  1005.     long n,i;
  1006.     static const char here[]="PrintAnAssignmentOfElement";
  1007.     
  1008.     if(index<0 || index>=Elements(d))return assignSubscriptBoundsError;
  1009.     w=*d;
  1010.     for(i=0;i<ASSIGN_DIMS;i++)w.dim[i]=0;
  1011.     w.firstElement=0;
  1012.     w.comment=NULL;
  1013.     w.name=(char *)malloc(strlen(w.name)+16*Dims(d));
  1014.     if(w.name==NULL){
  1015.         if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1016.             ,here,(long)strlen(w.name)+16*Dims(d));
  1017.         else return assignMemoryError;
  1018.     }
  1019.     ElementName(w.name,d,index);
  1020.     w.ptr=ElementPtr(d,index);
  1021.     if(IsPtr(w.type))w.type+=charType-charPtrType;
  1022.     if(IsString(w.type))w.malloced=0;
  1023.     /*  Turn off checking, 'cause subscripted name is illegal. */
  1024.     n=PrintAnAssignment(stream,&w,flags|assignAlreadyChecked);
  1025.     free(w.name);
  1026.     return n;
  1027. }
  1028.  
  1029. long PrintAnAssignment(FILE *stream,Description *d,short flags)
  1030. {
  1031.     short hexEncode;
  1032.     char *string,name[32+16*ASSIGN_DIMS];
  1033.     static const char here[]="PrintAnAssignment";
  1034.     long index,n=0,row;
  1035.     int error;
  1036.     static short lineLength;/* zeroed before printing array or string and after \n. */
  1037.     Description w;
  1038.     
  1039.     error=CheckADescription(d,here,flags);
  1040.     if(error<0)return error;
  1041.     if(ElementPtr(d,0)==NULL)return n;
  1042.     if(IsPtr(d->type) || d->dim[0]){
  1043.         if(Dims(d))row=d->dim[Dims(d)-1];
  1044.         else row=1;
  1045.         hexEncode=IsIntegral(d->type) && !(flags&assignNoHexInts)
  1046.             &&(row>2+typeSize[d->type]);
  1047.         hexEncode|=IsFloating(d->type)&&(flags&assignHexFloats);
  1048.         if(hexEncode){
  1049.             for(index=0;index<Elements(d);index+=row){
  1050.                 lineLength=0;
  1051.                 w=*d;
  1052.                 if(IsString(w.type))w.malloced=0;
  1053.                 if(row>1)w.dim[Dims(d)-1]=0;
  1054.                 ElementName(name,&w,index/row);
  1055.                 lineLength+=fprintf(stream,"%s=",name);
  1056.                 string=BinaryToHex(row*typeSize[d->type]
  1057.                     ,ElementPtr(d,index),NULL);
  1058.                 if(string==NULL){
  1059.                     if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1060.                         ,here,2L*row*typeSize[d->type]);
  1061.                     else return assignMemoryError;
  1062.                 }
  1063.                 PrintQuotedString(stream,&lineLength,string,"\"","\"");
  1064.                 free(string);
  1065.                 lineLength+=fprintf(stream,";");
  1066.                 if(index<Elements(d)-row)lineLength+=fprintf(stream,"\\" NL);
  1067.             }
  1068.             n=Elements(d);
  1069.         }else{
  1070.             long i,assignLength,oldLineLength,elements;
  1071.             
  1072.             lineLength=0;
  1073.             elements=Elements(d);
  1074.             for(i=0;i<elements;i++){
  1075.                 oldLineLength=lineLength;
  1076.                 n+=PrintAnAssignmentOfElement(stream,d,i,flags);
  1077.                 assignLength=lineLength-oldLineLength;
  1078.                 if(lineLength+assignLength+1>=MAX_LINELENGTH && i<elements-1){
  1079.                     fprintf(stream,"\\" NL);
  1080.                     lineLength=0;
  1081.                     lineLength+=fprintf(stream,"    ");
  1082.                 }
  1083.             }
  1084.         }
  1085.         if(d->comment!=NULL && lineLength+strcspn(d->comment,NL)+6>=MAX_LINELENGTH){
  1086.             fprintf(stream,"\\" NL);
  1087.             lineLength=0;
  1088.             lineLength+=fprintf(stream,"    ");
  1089.         }
  1090.         goto comment;
  1091.     }
  1092.     assert(d->dim[0]==0);
  1093.     if(IsString(d->type))lineLength=0;
  1094. fflush(stream);
  1095.     lineLength+=fprintf(stream,"%s=",d->name);
  1096. fflush(stream);
  1097.     n=1;
  1098.     switch(d->type){
  1099.     case charType:
  1100.         lineLength+=fprintf(stream,"%ld;",(long)*(char *)d->ptr);
  1101.         break;
  1102.     case unsignedCharType:
  1103.         lineLength+=fprintf(stream,"%lu;",(unsigned long)*(unsigned char *)d->ptr);
  1104.         break;
  1105.     case shortType:
  1106.         lineLength+=fprintf(stream,"%ld;",(long)*(short *)d->ptr);
  1107.         break;
  1108.     case unsignedShortType:
  1109.         lineLength+=fprintf(stream,"%lu;",(unsigned long)*(unsigned short *)d->ptr);
  1110.         break;
  1111.     case longType:
  1112.         lineLength+=fprintf(stream,"%ld;",*(long *)d->ptr);
  1113.         break;
  1114.     case unsignedLongType:
  1115.         lineLength+=fprintf(stream,"%lu;",*(unsigned long *)d->ptr);
  1116.         break;
  1117.     case floatType:
  1118.     #if SHORT_DOUBLE_OK
  1119.     case shortDoubleType:
  1120.     #endif
  1121.     case doubleType:
  1122.         if(flags&assignHexFloats){
  1123.             string=BinaryToHex(typeSize[d->type],d->ptr,NULL);
  1124.             if(string==NULL){
  1125.                 if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1126.                     ,here,2L*typeSize[d->type]);
  1127.                 else return assignMemoryError;
  1128.             }
  1129.             lineLength+=fprintf(stream,"\"%s\";",string);
  1130.             free(string);
  1131.         }else switch(d->type){
  1132.             case floatType: 
  1133.                 lineLength+=fprintf(stream,"%.*g;",FLT_DIG,*(float *)d->ptr);
  1134.                 break;
  1135.             #if SHORT_DOUBLE_OK
  1136.                 case shortDoubleType: 
  1137.                     lineLength+=fprintf(stream,"%.*g;",DBL_DIG,*(short double *)d->ptr);
  1138.                     break;
  1139.             #endif
  1140.             case doubleType: 
  1141.                 lineLength+=fprintf(stream,"%.*g;",DBL_DIG,*(double *)d->ptr);
  1142.                 break;
  1143.         }
  1144.         break;
  1145.     case stringType:
  1146.         PrintQuotedString(stream,&lineLength,*(const char **)d->ptr,"\"","\"");
  1147.         lineLength+=fprintf(stream,";");
  1148.         break;
  1149.     default:
  1150.         /*  It shouldn't be possible to arrive here. */
  1151.         PrintfExit("PrintAnAssignment: oops. \"%s\" of type (%s) fell through a crack.\n"
  1152.             ,d->name,typeName[d->type]);
  1153.         n=0;
  1154.     }
  1155. comment:
  1156.     if(d->comment!=NULL && !(flags&assignNoComment))
  1157.         lineLength+=fprintf(stream," /* %s */",d->comment);
  1158.     return n;
  1159. }
  1160.  
  1161. int AssignmentLineWasBlank(void)
  1162. {
  1163.     extern int lineWasBlank;
  1164.  
  1165.     return lineWasBlank;
  1166. }
  1167.  
  1168. long ReadAssignmentFile(const char *filename,Description d[],short flags)
  1169. {
  1170.     long n=0;
  1171.     FILE *stream;
  1172.     static const char here[]="ReadAssignmentFile";
  1173.     
  1174.     stream=fopen(filename,"r");
  1175.     if(stream==NULL)return assignFileError;
  1176.     n=ReadAssignmentStream(stream,d,flags);
  1177.     fclose(stream);
  1178.     return n;
  1179.     
  1180.     #if 0
  1181.     if(!(flags&assignLocalCall)){
  1182.             /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1183.             ReadAssignmentFile(filename,d,flags|assignScan|assignLocalCall);
  1184.             flags&=~assignScan;
  1185.             flags|=assignLocalCall;
  1186.             error=AllocateDescribedPtrVars(d,here,flags);
  1187.             if(error)return error;
  1188.             /* Second pass: read data again, including the newly allocated Ptr variables */
  1189.         }
  1190.         stream=fopen(filename,"r");
  1191.         if(stream==NULL)return assignFileError;
  1192.         do{
  1193.             int i;
  1194.             i=ReadAssignmentLine(stream,d,flags);
  1195.             if(i>=0)n+=i;
  1196.             else{
  1197.                 n=i;
  1198.                 break;
  1199.             }
  1200.         }while(!feof(stream));
  1201.         fclose(stream);
  1202.         return n;
  1203.     #endif
  1204. }
  1205.  
  1206. long ReadAssignmentStream(FILE *stream,Description d[],short flags)
  1207. {
  1208.     long n=0;
  1209.     int error;
  1210.     static const char here[]="ReadAssignmentStream";
  1211.     
  1212.     if(!(flags&assignLocalCall)){
  1213.         /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1214.         long position=ftell(stream);
  1215.         ReadAssignmentStream(stream,d,flags|assignScan|assignLocalCall);
  1216.         flags&=~assignScan;
  1217.         flags|=assignLocalCall;
  1218.         fseek(stream,position,SEEK_SET);
  1219.         error=AllocateDescribedPtrVars(d,here,flags);
  1220.         if(error)return error;
  1221.         /* Second pass: read data again, including the newly allocated Ptr variables */
  1222.     }
  1223.     do{
  1224.         int i;
  1225.         i=ReadAssignmentLine(stream,d,flags);
  1226.         if(i>=0)n+=i;
  1227.         else{
  1228.             n=i;
  1229.             break;
  1230.         }
  1231.     }while(!feof(stream));
  1232.     return n;
  1233. }
  1234.  
  1235. long ReadAssignmentBlock(FILE *stream,Description d[],short flags)
  1236. {
  1237.     long n=0;
  1238.     int error;
  1239.     static const char here[]="ReadAssignmentBlock";
  1240.     
  1241.     if(!(flags&assignLocalCall)){
  1242.         /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1243.         long position=ftell(stream);
  1244.         ReadAssignmentBlock(stream,d,flags|assignScan|assignLocalCall);
  1245.         flags&=~assignScan;
  1246.         flags|=assignLocalCall;
  1247.         fseek(stream,position,SEEK_SET);
  1248.         error=AllocateDescribedPtrVars(d,here,flags);
  1249.         if(error)return error;
  1250.         /* Second pass: read data again, including the newly allocated Ptr variables */
  1251.         n=ReadAssignmentBlock(stream,d,flags);
  1252.         return n;
  1253.     }
  1254.     do{
  1255.         int i;
  1256.         i=ReadAssignmentLine(stream,d,flags);
  1257.         if(i>=0)n+=i;
  1258.         else{
  1259.             n=i;
  1260.             break;
  1261.         }
  1262.     }while(!AssignmentLineWasBlank());
  1263.     return n;
  1264. }
  1265.  
  1266. static char *lineBuffer;    /* global because it would be messy to pass this address to
  1267.                             all the routines below that need to call ParsingError */
  1268. long ReadAssignmentLine(FILE *stream,Description description[],short flags)
  1269. {
  1270.     register Description *d;
  1271.     Description *vMatch;
  1272.     long j,n=0,nAssign;
  1273.     int i;
  1274.     char *s,*sOld;
  1275.     long ftellOld;
  1276.     void *ptr;
  1277.     extern int lineWasBlank;
  1278.     int hexAllowed,subscriptDims,outOfBounds,unknown;
  1279.     long subscript[ASSIGN_DIMS],index;
  1280.     int error;
  1281.     Description scratchDescription,*dScratch;
  1282.     double scratchDouble;
  1283.     long scratchLong;
  1284.     void *scratchPtr;
  1285.     static const char here[]="ReadAssignmentLine";
  1286.     
  1287.     if(!(flags&assignLocalCall)){
  1288.         /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1289.         long position=ftell(stream);
  1290.         ReadAssignmentLine(stream,description,flags|assignScan|assignLocalCall);
  1291.         flags&=~assignScan;
  1292.         flags|=assignLocalCall;
  1293.         fseek(stream,position,SEEK_SET);
  1294.         error=AllocateDescribedPtrVars(description,here,flags);
  1295.         if(error)return error;
  1296.         /* Second pass: read data again, including the newly allocated Ptr variables */
  1297.     }
  1298.     if(stream==NULL)return 0;
  1299.     error=CheckDescriptions(description,here,flags);
  1300.     if(error)return error;
  1301.     lineWasBlank=TRUE;
  1302.     lineBuffer=(char *)malloc(BUFFER_SIZE);
  1303.     if(lineBuffer==NULL){
  1304.         if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1305.             ,here,(long)BUFFER_SIZE);
  1306.         else return assignMemoryError;
  1307.     }
  1308.     dScratch=&scratchDescription;
  1309.     s=NULL;
  1310.     do{
  1311.         /* parse the name */
  1312.         if(s==NULL)
  1313.             s=NextTokenInThisOrNextLine(stream,lineBuffer,s,flags&~assignNeedMore);
  1314.         else s=NextTokenInLine(stream,lineBuffer,s,flags);
  1315.         if(s==NULL){
  1316.             free(lineBuffer);
  1317.             return n;
  1318.         }
  1319.         *dScratch=Describe(unknownType,&scratchPtr,GetName(&s,flags),NULL);
  1320.         if(dScratch->name==NULL){
  1321.             free(lineBuffer);
  1322.             return assignMemoryError;
  1323.         }
  1324.         dScratch->sizedOnce=dScratch->sized=dScratch->malloced=0;
  1325.         d=dScratch;
  1326.         unknown=1;
  1327.         for(vMatch=&description[0];vMatch->type!=0;vMatch++){
  1328.             if(streq(dScratch->name,vMatch->name)){
  1329.                 d=vMatch;
  1330.                 unknown=0;
  1331.                 break;
  1332.             }
  1333.         }
  1334.         if(unknown && (flags&assignReportUnknown)){
  1335.             if(PRINTF_EXIT)ParsingError(s,"unknown variable");
  1336.             else {
  1337.                 free(dScratch->name);    /* 7/16/94 */
  1338.                 free(lineBuffer);
  1339.                 return assignUnknownVariableError;
  1340.             }
  1341.         }
  1342.         hexAllowed=IsIntegral(d->type)&&!(flags&assignNoHexInts)
  1343.             || IsFloating(d->type)&&(flags&assignHexFloats);
  1344.         s=NextToken(stream,lineBuffer,s,flags);
  1345.         subscriptDims=0;
  1346.         for(i=0;i<ASSIGN_DIMS;i++)subscript[i]=0;
  1347.         outOfBounds=0;
  1348.         if(IsPtr(d->type) && !d->malloced)outOfBounds=1;
  1349.  
  1350.         /* parse subscripts, e.g. [123][3] */
  1351.         for(i=0;i<Dims(d) || !d->sized;i++){
  1352.             /* parse a subscript, e.g. [123] */
  1353.             if(*s != '['){
  1354.                 if(hexAllowed && subscriptDims==Dims(d)-1)break;
  1355.                 if(!d->sized)break;
  1356.                 if(PRINTF_EXIT)ParsingError(s,"expected \"[\"");
  1357.                 else {
  1358.                     free(dScratch->name);    /* 7/16/94 */
  1359.                     free(lineBuffer);
  1360.                     return assignSubscriptError;
  1361.                 }
  1362.             }
  1363.             if(i==ASSIGN_DIMS){
  1364.                 if(PRINTF_EXIT)ParsingError(s,"too many dimensions");
  1365.                 else {
  1366.                     free(dScratch->name);    /* 7/16/94 */
  1367.                     free(lineBuffer);
  1368.                     return assignSubscriptError;
  1369.                 }
  1370.             }
  1371.             s++;
  1372.             s=NextToken(stream,lineBuffer,s,flags);
  1373.             sOld=s;
  1374.             ftellOld=ftell(stream);
  1375.             subscript[i]=strtolN(s,&s,0);
  1376.             subscriptDims=i+1;
  1377.             if(s==sOld && ftellOld==ftell(stream)){
  1378.                 if(PRINTF_EXIT)ParsingError(s,"expected subscript");
  1379.                 else {
  1380.                     free(dScratch->name);    /* 7/16/94 */
  1381.                     free(lineBuffer);
  1382.                     return assignSubscriptError;
  1383.                 }
  1384.             }
  1385.             j=subscript[i];
  1386.             if(i==Dims(d)-1)j-=d->firstElement;
  1387.             if(j<0 || d->sized && j>=d->dim[i]){
  1388.                 if(flags&assignReportUnknown){
  1389.                     if(PRINTF_EXIT)ParsingError(s,"subscript out of bounds: %s"
  1390.                         ,DescriptionNameDimensions(d));
  1391.                     else {
  1392.                         free(dScratch->name);    /* 7/16/94 */
  1393.                         free(lineBuffer);
  1394.                         return assignSubscriptBoundsError;
  1395.                     }
  1396.                 }
  1397.                 outOfBounds=1;
  1398.             }
  1399.             s=NextToken(stream,lineBuffer,s,flags);
  1400.             if(*s != ']'){
  1401.                 if(PRINTF_EXIT)ParsingError(s,"expected \"]\"");
  1402.                 else {
  1403.                     free(dScratch->name);    /* 7/16/94 */
  1404.                     free(lineBuffer);
  1405.                     return assignSubscriptError;
  1406.                 }
  1407.             }
  1408.             s++;
  1409.             s=NextToken(stream,lineBuffer,s,flags);
  1410.         }
  1411.  
  1412.         /* parse the equal sign */
  1413.         if(*s != '='){
  1414.             if(PRINTF_EXIT)ParsingError(s,"expected \"=\"");
  1415.             else {
  1416.                 free(dScratch->name);    /* 7/16/94 */
  1417.                 free(lineBuffer);
  1418.                 return assignEqualsError;
  1419.             }
  1420.         }
  1421.         s++;
  1422.         
  1423.         /* parse the value */
  1424.         s=NextToken(stream,lineBuffer,s,flags);
  1425.         if(d->sized && Dims(d)!=subscriptDims 
  1426.             && !(*s=='"'  && Dims(d)-1==subscriptDims)){
  1427.             if(PRINTF_EXIT)
  1428.                 ParsingError(s,"\"%s\": wrong number of subscripts",DescriptionNameDimensions(d));
  1429.             else {
  1430.                 free(dScratch->name);    /* 7/16/94 */
  1431.                 free(lineBuffer);
  1432.                 return assignSubscriptError;
  1433.             }
  1434.         }
  1435.         if(d->type==unknownType){
  1436.             /*  choose type that can accept the data, so we can parse and discard it. */
  1437.             if(*s=='"')d->type=stringType;
  1438.             else if(streq2(s,"0x") || streq2(s,"0X") || *s=='\''
  1439.                 || !(flags&assignNoHexInts) && *s=='"')d->type=longType;
  1440.             else d->type=doubleType;
  1441.         }
  1442.         if(unknown || outOfBounds){
  1443.             /* use scratch scalar to receive the assignment */
  1444.             short type=d->type;
  1445.             if(IsPtr(type))type+=charType-charPtrType;
  1446.             if(1){
  1447.                 /* 11/8/95 new code. Retain the allocated dScratch->name */
  1448.                 if(IsString(type))
  1449.                     *dScratch=Describe(type,&scratchPtr,dScratch->name,NULL);
  1450.                 else if(IsIntegral(type))
  1451.                     *dScratch=Describe(type,&scratchLong,dScratch->name,NULL);
  1452.                 else if(IsFloating(type))
  1453.                     *dScratch=Describe(type,&scratchDouble,dScratch->name,NULL);
  1454.             }else{
  1455.                 /* old code. Lost the allocated dScratch->name, which wreaked havoc when we later free() it. */
  1456.                 if(IsString(type))
  1457.                     *dScratch=Describe(type,&scratchPtr,d->name,NULL);
  1458.                 else if(IsIntegral(type))
  1459.                     *dScratch=Describe(type,&scratchLong,d->name,NULL);
  1460.                 else if(IsFloating(type))
  1461.                     *dScratch=Describe(type,&scratchDouble,d->name,NULL);
  1462.             }
  1463.             d=dScratch;
  1464.             index=0;
  1465.             /* Note: d is now inconsistent with subscript and subscriptDims */
  1466.         }else index=ElementIndex(d,subscript);
  1467.         ptr=ElementPtr(d,index);
  1468.         sOld=s;
  1469.         ftellOld=ftell(stream);
  1470.         nAssign=1;
  1471.         error=0;
  1472.         if(*s=='"' && !IsString(d->type) && hexAllowed){
  1473.             long bytes,elements;
  1474.             if(unknown || outOfBounds)
  1475.                 nAssign=HexAssignment(stream,lineBuffer,&s,d,0,index,&bytes,flags);
  1476.             else nAssign=HexAssignment(stream,lineBuffer,&s,d,subscriptDims,index
  1477.                 ,&bytes,flags);
  1478.             if(!unknown){
  1479.                 elements=bytes/typeSize[vMatch->type];
  1480.                 if(elements>1){
  1481.                     subscript[subscriptDims++]=elements-1;
  1482.                     error=GetDimensions(vMatch,subscriptDims,subscript,flags);
  1483.                     subscript[--subscriptDims]=0;
  1484.                 }else error=GetDimensions(vMatch,subscriptDims,subscript,flags);
  1485.             }
  1486.         }else{
  1487.             if(!unknown)error=GetDimensions(vMatch,subscriptDims,subscript,flags);
  1488.             else error=0;
  1489.             if(!error)switch(d->type){
  1490.             default:
  1491.                 assert(0 /* It shouldn't be possible to arrive here. */);
  1492.                 nAssign=0;
  1493.                 break;
  1494.             case charType:
  1495.             case charPtrType:
  1496.                 *(char *)ptr=strtolN(s,&s,0);
  1497.                 break;
  1498.             case unsignedCharType:
  1499.             case unsignedCharPtrType:
  1500.                 *(unsigned char *)ptr=strtoulN(s,&s,0);
  1501.                 break;
  1502.             case shortType:
  1503.             case shortPtrType:
  1504.                 *(short *)ptr=strtolN(s,&s,0);
  1505.                 break;
  1506.             case unsignedShortType:
  1507.             case unsignedShortPtrType:
  1508.                 *(unsigned short *)ptr=strtoulN(s,&s,0);
  1509.                 break;
  1510.             case longType:
  1511.             case longPtrType:
  1512.                 *(long *)ptr=strtolN(s,&s,0);
  1513.                 break;
  1514.             case unsignedLongType:
  1515.             case unsignedLongPtrType:
  1516.                 *(unsigned long *)ptr=strtoulN(s,&s,0);
  1517.                 break;
  1518.             case floatType:
  1519.             case floatPtrType:
  1520.                 *(float *)ptr=strtodN(s,&s);
  1521.                 break;
  1522.             #if SHORT_DOUBLE_OK
  1523.                 case shortDoubleType:
  1524.                 case shortDoublePtrType:
  1525.                     *(short double *)ptr=strtodN(s,&s);
  1526.                     break;
  1527.             #endif
  1528.             case doubleType:
  1529.             case doublePtrType:
  1530.                 *(double *)ptr=strtodN(s,&s);
  1531.                 break;
  1532.             case stringType:
  1533.                 if(d->malloced && !d->dim[0])free(*(char **)ptr);
  1534.                 *(char **)ptr=GetQuotedString(stream,lineBuffer,s,&s,flags);
  1535.                 d->malloced=1;
  1536.                 break;
  1537.             }
  1538.         }
  1539.         if(error==assignInconsistentDimensionsError){
  1540.             if(PRINTF_EXIT)ParsingError(s,"inconsistent dimensionality of \"%s\""
  1541.                 ,DescriptionNameDimensions(d));
  1542.             else {
  1543.                 free(dScratch->name);    /* 7/16/94 */
  1544.                 free(lineBuffer);
  1545.                 return error;
  1546.             }
  1547.         }
  1548.         if(s==sOld && ftellOld==ftell(stream)){
  1549.             if(PRINTF_EXIT)ParsingError(s,"expected %s",typeName[d->type]);
  1550.             else {
  1551.                 free(dScratch->name);    /* 7/16/94 */
  1552.                 free(lineBuffer);
  1553.                 return assignConstantError;
  1554.             }
  1555.         }
  1556.  
  1557.         /* parse the semicolon */
  1558.         s=NextToken(stream,lineBuffer,s,flags);
  1559.         if(*s!=';'){
  1560.             if(PRINTF_EXIT)ParsingError(s,"expected \";\"");
  1561.             else {
  1562.                 free(dScratch->name);    /* 7/16/94 */
  1563.                 free(lineBuffer);
  1564.                 return assignSemicolonError;
  1565.             }
  1566.         }
  1567.         s++;
  1568.         
  1569.         if(ECHO_ASSIGNMENTS){
  1570.             if(unknown)printf("/*UNKNOWN: ");
  1571.             else if(outOfBounds)printf("/*OUT OF BOUNDS: ");
  1572.             if(!subscriptDims)PrintAnAssignment(stdout,d
  1573.                 ,(flags&~assignHexFloats)|assignNoComment);
  1574.             else PrintAnAssignmentOfElement(stdout,d,index
  1575.                 ,(flags&~assignHexFloats)|assignNoComment);
  1576.             if(outOfBounds || unknown)printf("*/ ");
  1577.         }
  1578.         free(dScratch->name);    /* 7/16/94 */
  1579.         dScratch->name=NULL;
  1580.         FreeADescribedVar(dScratch,flags);
  1581.         if(d!=dScratch)n+=nAssign;
  1582.     } while(1);
  1583. }
  1584.  
  1585. char *GetName(char **sPtr,short flags)
  1586. /*  Gets a variable's name.  */
  1587. {
  1588.     char *s,*sEnd,*name;
  1589.     long bytes=0,i;
  1590.     static const char here[]="ReadAssignmentLine/CheckADescription:GetName";
  1591.     
  1592.     s=*sPtr;
  1593.     if(ParseName(&s))if(PRINTF_EXIT)ParsingError(s,"expected name");
  1594.     sEnd=s;
  1595.     bytes=0;
  1596.     for(s=*sPtr;!isspace(*s) && s!=sEnd;s++)bytes++;
  1597.     name=(char *)malloc(bytes+1);
  1598.     if(name==NULL){
  1599.         if(PRINTF_EXIT)ParsingError(s,"%s: no room for %ld bytes.\n"
  1600.             ,here,bytes+1);
  1601.         else return NULL;
  1602.     }
  1603.     for(i=0;i<bytes;i++){
  1604.         while(isspace((*sPtr)[i]))(*sPtr)++;
  1605.         name[i]=(*sPtr)[i];
  1606.     }
  1607.     name[bytes]=0;
  1608.     *sPtr=s;
  1609.     return name;
  1610. }
  1611.  
  1612. int ParseName(char **sPtr)
  1613. /*
  1614. Parse a variable's name. It may be just a C identifier: alphabetic (or _)
  1615. followed by any number of alphanumeric (or _). Or it may be made up of multiple
  1616. identifiers joined by "." or "->" infix operators, e.g. "LP->p". 
  1617. */
  1618. {
  1619.     char *s;
  1620.     int error=0;
  1621.     
  1622.     s=*sPtr;
  1623.     while(*s!=0){
  1624.         for(;isspace(*s);s++) ;
  1625.         if(!(isalpha(*s)||*s=='_')){
  1626.             error=1;
  1627.             break;
  1628.         }
  1629.         for(;isalnum(*s)||*s=='_';s++) ;
  1630.         for(;isspace(*s);s++) ;
  1631.         if(*s=='.'){
  1632.             s++;
  1633.             continue;
  1634.         }
  1635.         if(streq2(s,"->")){
  1636.             s+=2;
  1637.             continue;
  1638.         }
  1639.         break;
  1640.     }
  1641.     *sPtr=s;
  1642.     return error;
  1643. }
  1644.  
  1645. long HexAssignment(FILE *stream,char *lineBuffer,char **sPtr,Description *d
  1646.     ,int subscriptDims,long index,long *bytesPtr,short flags)
  1647. {
  1648.     char *string,*s;
  1649.     long bytes,excess,n;
  1650.     int error;
  1651.     void *ptr;
  1652.     
  1653.     if(!(flags&assignScan) 
  1654.         && (subscriptDims<Dims(d)-1 || subscriptDims>Dims(d)))return assignSubscriptError;
  1655.     ptr=ElementPtr(d,index);
  1656.     /*
  1657.     Read in hex data.
  1658.     For scalar insist on exactly the right number of bytes.
  1659.     For array, if flags&assignReportUnknown then insist on exactly the right number
  1660.     of bytes; otherwise merely insist that the binary object size be a 
  1661.     multiple of the data element size.
  1662.     */
  1663.     s=*sPtr;
  1664.     string=GetQuotedString(stream,lineBuffer,s,&s,flags);
  1665.     *bytesPtr=strlen(string)/2;
  1666.     bytes=typeSize[d->type];
  1667.     if(subscriptDims==Dims(d)-1)bytes*=d->dim[subscriptDims];
  1668.     excess=strlen(string)-2*bytes;
  1669.     if(subscriptDims==Dims(d)-1 && !(excess>0 && (flags&assignReportUnknown)))
  1670.         excess=strlen(string)%(2*typeSize[d->type]);
  1671.     if(excess!=0 && !(flags&assignScan)){
  1672.         free(string);
  1673.         if(PRINTF_EXIT){
  1674.             if(excess>0)ParsingError(s,"\"%s=\", %ld extra hex digits",d->name,excess);
  1675.             else ParsingError(s,"\"%s=\", %ld too few hex digits",d->name,-excess);
  1676.         }else return assignHexError;
  1677.     }
  1678.     string[strlen(string)-strlen(string)%(2*typeSize[d->type])]=0;
  1679.     if(strlen(string)>2*bytes)string[2*bytes]=0;
  1680.     if(ptr!=NULL){
  1681.         error=HexToBinary(string,ptr);
  1682.         if(error){
  1683.             if(PRINTF_EXIT)ParsingError(s,"\"%s=\", hex string contains non hex char:\n\"%s\"\n"
  1684.                 ,d->name,string);
  1685.             else {
  1686.                 free(string);
  1687.                 return assignHexError;
  1688.             }
  1689.         }
  1690.         n=strlen(string)/(2*typeSize[d->type]);    /*  number of assigments */
  1691.     }else n=0;
  1692.     free(string);
  1693.     *sPtr=s;
  1694.     return n;
  1695. }
  1696.  
  1697. char *GetQuotedString(FILE *stream,char *lineBuffer,char *s,char **sPtr,short flags)
  1698. /*
  1699. Returns a newly malloced string copied from the lineBuffer (starting at s),
  1700. supplemented if necessary, by reading in further lines from the stream. The
  1701. translation from input to output mimics what would be performed by a C compiler.
  1702. Each line of input must fit in the BUFFER_SIZE byte lineBuffer, but the
  1703. resulting string (concatenated across continuations and adjacent strings) can be
  1704. of any length, limited only by how much space can be obtained by malloc. Returns
  1705. NULL if an error occurs (e.g. can't malloc needed space).
  1706. */
  1707. {
  1708.     char *newS,*oldString,*newString;
  1709.     size_t stringSize;
  1710.     long j;
  1711.     static const char here[]="GetQuotedString";
  1712.     
  1713.     /* parse the opening quote mark */
  1714.     if(*s!='"'){
  1715.         if(PRINTF_EXIT)ParsingError(s,"expected '\"'");
  1716.         else return NULL;
  1717.     }
  1718.     s++;
  1719.     stringSize=0;
  1720.     newS=newString=NULL;
  1721.     do{
  1722.         /* copy string, translating any backslash escapes */
  1723.         while(1){
  1724.             j=strcspn(s,"\\\"");
  1725.             if(stringSize==0 || strlen(newString)+j+2>stringSize){
  1726.                 /*  allocate a string that's big enough */
  1727.                 stringSize+=j;
  1728.                 stringSize*=2;
  1729.                 if(stringSize<BUFFER_SIZE)stringSize=BUFFER_SIZE;
  1730.                 oldString=newString;
  1731.                 newString=malloc(stringSize);
  1732.                 if(newString==NULL){
  1733.                     if(oldString!=NULL)free(oldString);
  1734.                     if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1735.                         ,here,(long)stringSize);
  1736.                     else return NULL;
  1737.                 }
  1738.                 if(oldString!=NULL){
  1739.                     strcpy(newString,oldString);
  1740.                     free(oldString);
  1741.                 }
  1742.                 newS+=newString-oldString;
  1743.             }
  1744.             strncpy(newS,s,j);
  1745.             s+=j;
  1746.             newS+=j;
  1747.             *newS=0;
  1748.             if(streq2(s,"\\\r") || streq2(s,"\\\n")){
  1749.                 s+=2;
  1750.                 continue;
  1751.             }
  1752.             if(*s=='\\'){
  1753.                 *newS++=strtoc(s,&s);
  1754.                 *newS=0;
  1755.             }else break;
  1756.         };
  1757.         if(strlen(s)==0){
  1758.             s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags|assignNeedMore);
  1759.             continue;
  1760.         }
  1761.         /* parse the closing quote mark */
  1762.         if(*s!='"'){
  1763.             if(PRINTF_EXIT)ParsingError(s,"expected '\"'");
  1764.             else{
  1765.                 free(newString);
  1766.                 return NULL;
  1767.             }
  1768.         }
  1769.         s++;
  1770.         
  1771.         /* look for another opening quote mark. Concatenate adjacent strings */
  1772.         s=NextToken(stream,lineBuffer,s,flags);
  1773.         if(*s!='"')break;
  1774.         s++;
  1775.     }while(1);
  1776.     /* shrink string allocation down to what we actually used */
  1777.     stringSize=1+strlen(newString);
  1778.     newString=realloc(newString,stringSize);
  1779.     if(newString==NULL){
  1780.         if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1781.             ,here,(long)stringSize);
  1782.         else return NULL;
  1783.     }
  1784.     *sPtr=s;
  1785.     return newString;
  1786. }
  1787.  
  1788. void ParsingError(char *s,char *format,...)
  1789. {
  1790.     va_list args;
  1791.     long i;
  1792.     static short lineLength=0;/* zeroed after each \n. */
  1793.  
  1794.     printf("\007\nReadAssignmentLine: ");
  1795.     va_start(args,format);
  1796.     #if !MATLAB
  1797.         vfprintf(stdout,format,args);
  1798.     #else
  1799.         {
  1800.             char string[256];
  1801.  
  1802.             vsprintf(string,format,args);
  1803.             printf("%s",string);
  1804.         }
  1805.     #endif
  1806.     va_end(args);
  1807.     if(s==NULL)PrintfExit(".\n");
  1808.     printf(". Quitting at |.\n");
  1809.     if(0){
  1810.         printf("%s",lineBuffer);
  1811.         if(lineBuffer[strlen(lineBuffer)-1]!=NEWLINE)printf("...\n");
  1812.     }
  1813.     if(lineBuffer[strlen(lineBuffer)-1]==NEWLINE)lineBuffer[strlen(lineBuffer)-1]=0;
  1814.     PrintQuotedString(stdout,&lineLength,lineBuffer,"\"","\"\n");
  1815.     s[0]='|';
  1816.     s[1]=0;
  1817.     s=lineBuffer;
  1818.     for(i=0;i<strlen(s)-1;i++)if(isgraph(s[i]))s[i]=' ';
  1819.     if(0)PrintfExit("%s\n",lineBuffer);
  1820.     PrintQuotedString(stdout,&lineLength,lineBuffer," ","\n");
  1821.     PrintfExit("");
  1822. }
  1823.  
  1824. char *ReadLine(char *lineBuffer,size_t bufferSize,FILE *stream,short flags)
  1825. /*  The Boolean global lineWasBlank is initialized to true in ReadAssignmentLine(),  */
  1826. /*  falsified (if appropriate) here, and returned by AssignmentLineWasBlank(). */
  1827. {
  1828.     char *s;
  1829.     static const char whiteSpace[]=" \t\n\r\v\f";
  1830.     extern int lineWasBlank;
  1831.     
  1832.     s=fgets(lineBuffer,bufferSize,stream);
  1833.     if(s==NULL){
  1834.         if(flags&assignNeedMore){
  1835.             if(PRINTF_EXIT)ParsingError(s,"premature end of stream");
  1836.             else return NULL;
  1837.         }else return NULL;
  1838.     }
  1839.     if(strlen(s)!=strspn(s,whiteSpace))lineWasBlank=FALSE;
  1840.     if(strlen(s)>=bufferSize-1 && s[strlen(s)-1]!=NEWLINE){
  1841.         if(PRINTF_EXIT){
  1842.             s+=strlen(s);
  1843.             ParsingError(s,"line exceeds buffer size");
  1844.         }else return NULL;
  1845.     }
  1846.     if(ECHO_FILE)printf("%s",s);
  1847.     return s;
  1848. }
  1849.  
  1850. char *NextToken(FILE *stream,char *lineBuffer,char *s,short flags)
  1851. /*
  1852. Skip past white space and comments to the beginning of the next token,
  1853. reading as many lines as necessary.
  1854. */
  1855. {
  1856.     do{
  1857.         s=NextTokenInThisOrNextLine(stream,lineBuffer,s,flags|assignNeedMore);
  1858.         if(s!=NULL || feof(stream))return s;
  1859.     }while(1);
  1860. }
  1861.  
  1862. char *NextTokenInThisOrNextLine(FILE *stream,char *lineBuffer,char *s,short flags)
  1863. /*
  1864. Skip past white space and comments to the beginning of the next token,
  1865. reading one line if necessary. 
  1866. */
  1867. {
  1868.     s=NextTokenInLine(stream,lineBuffer,s,flags);
  1869.     if(s!=NULL)return s;
  1870.     s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags);
  1871.     s=NextTokenInLine(stream,lineBuffer,s,flags);
  1872.     return s;
  1873. }
  1874.  
  1875. char *NextTokenInLine(FILE *stream,char *lineBuffer,char *s,short flags)
  1876. /*
  1877. Skip past white space and comments to the beginning of the next token. NextTokenInLine
  1878. will read in new lines only if they are continuations of the current line (i.e.
  1879. it ends in "\") or if new lines are needed to find the end of an unfinished
  1880. comment. Input from stream is buffered one line at a time in lineBuffer, and s
  1881. points to the next unparsed character in that buffer.
  1882. */
  1883. {
  1884.     static const char whiteSpace[]=" \t\n\r\v\f";
  1885.     char *sTemp;
  1886.  
  1887.     do{
  1888.         /* skip white space and comments */
  1889.         do {
  1890.             if(s==NULL || strlen(s)==0)goto endOfLine;
  1891.             s+=strspn(s,whiteSpace);
  1892.             if(strlen(s)==0)goto endOfLine;
  1893.             if(streq2(s,"/*")){
  1894.                 do{
  1895.                     sTemp=s;
  1896.                     s=strstr(s,"*/");
  1897.                     if(s!=NULL)break;
  1898.                     if(ECHO_COMMENTS)printf("%s",sTemp);
  1899.                     s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags|assignNeedMore);
  1900.                 } while(1);
  1901.                 s+=2;
  1902.                 if(ECHO_COMMENTS){
  1903.                     s[-1]=0;
  1904.                     printf("%s/",sTemp);
  1905.                 }
  1906.                 continue;
  1907.             }
  1908.             if(streq2(s,"//")){
  1909.                 if(ECHO_COMMENTS){
  1910.                     if(s[strlen(s)-1]==NEWLINE)s[strlen(s)-1]=0;
  1911.                     printf("%s",s);
  1912.                 }
  1913.                 goto endOfLine;
  1914.             }
  1915.             if(streq2(s,"\\" NL)){
  1916.                 if(ECHO_COMMENTS || ECHO_ASSIGNMENTS)printf(" \\\n");
  1917.                 s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags&~assignNeedMore);
  1918.                 continue;
  1919.             }
  1920.             break;
  1921.         } while(1);
  1922.         return s;    /* normal return */
  1923.     }while(1);
  1924. endOfLine:
  1925.     if((ECHO_ASSIGNMENTS || ECHO_COMMENTS) && s!=NULL)printf("\n");
  1926.     return NULL;
  1927. }
  1928.  
  1929. double strtodN(char *s,char **sPtr)
  1930. /*
  1931. Supplement standard C routine strtod() by also handling NAN and INF. Any specification
  1932. of the NAN's type (1 to 255) is ignored, whether in THINK C style, e.g. NANFF, in
  1933. MPW C style, e.g. Nan[255], or Metrowerks CodeWarrior C style, e.g. NAN(021).
  1934. */
  1935. {
  1936.     if(streq2(s,"NAN")||streq2(s,"NaN")||streq2(s,"Nan")||streq2(s,"nan")){
  1937.         s+=3;
  1938.         s+=strspn(s,"0123456789abcdefABCDEF[]()");
  1939.         *sPtr=s;
  1940.         return NAN;
  1941.     }
  1942.     if(strncmp(s,"INF",3)==0||strncmp(s,"Inf",3)==0||strncmp(s,"inf",3)==0){
  1943.         s+=3;
  1944.         *sPtr=s;
  1945.         return INF;
  1946.     }
  1947.     if(strncmp(s,"-INF",4)==0||strncmp(s,"-Inf",4)==0||strncmp(s,"-inf",4)==0){
  1948.         s+=4;
  1949.         *sPtr=s;
  1950.         return -INF;
  1951.     }
  1952.     return strtod(s,sPtr);
  1953. }
  1954.  
  1955. long strtolN(char *s,char **sPtr,short flags)
  1956. /* Supplement standard C routine strtol() by handling quoted char, e.g. 'e' */
  1957. {
  1958.     long j;
  1959.     
  1960.     if(*s!='\'') return strtol(s,sPtr,flags);
  1961.     s++;
  1962.     j=strtoc(s,&s);
  1963.     if(*s!='\''){
  1964.         if(PRINTF_EXIT)ParsingError(s,"expected \"'\"");
  1965.         else return 0;
  1966.     }
  1967.     *sPtr=s+1;
  1968.     return j;
  1969. }    
  1970.  
  1971. unsigned long strtoulN(char *s,char **sPtr,short flags)
  1972. /* Supplement standard C routine by handling quoted char, e.g. 'e' */
  1973. {
  1974.     unsigned long j;
  1975.     
  1976.     if(*s!='\'') return strtoul(s,sPtr,flags);
  1977.     s++;
  1978.     j=strtoc(s,&s);
  1979.     j &= 255L;        /* strip off any sign extension, since char was signed */
  1980.     if(*s!='\''){
  1981.         if(PRINTF_EXIT)ParsingError(s,"expected \"'\"");
  1982.         else return 0;
  1983.     }
  1984.     *sPtr=s+1;
  1985.     return j;
  1986. }    
  1987.  
  1988. char strtoc(char *s,char **sPtr)
  1989. /* Extract a character from a string, translating backslash escapes */
  1990. {
  1991.     char c;
  1992.     
  1993.     startAgain:
  1994.     if(*s!='\\'){
  1995.         if(*s==0){
  1996.             *sPtr=s;
  1997.             return -1;
  1998.         }
  1999.         *sPtr=s+1;
  2000.         return *s;
  2001.     }
  2002.     s++;
  2003.     switch(s++[0]){
  2004.     case 'n': c='\n'; break;
  2005.     case 'r': c='\r'; break;
  2006.     case 't': c='\t'; break;
  2007.     case 'b': c='\b'; break;
  2008.     case 'v': c='\v'; break;
  2009.     case 'f': c='\f'; break;
  2010.     case 'a': c='\a'; break;
  2011.     case 'x': c=strtol(s,&s,16); break;
  2012.     case '0': case '1':case '2': case '3':case '4': case '5':case '6': case '7':
  2013.         c=strtol(s-1,&s,8); break;
  2014.     case '\r': case '\n': s++; goto startAgain;
  2015.     default: c=s[-1]; break;
  2016.     }
  2017.     *sPtr=s;
  2018.     return c;
  2019. }
  2020.  
  2021. void PrintQuotedString(FILE *stream,short *lineLengthPtr,const char *s
  2022.     ,const char *openQuote,const char *closeQuote)
  2023. {
  2024.     *lineLengthPtr+=fprintf(stream,openQuote);
  2025.     do{
  2026.         for(;*s!=0 && *lineLengthPtr+1+(isprint(*s)?1:(isspace(*s)?2:4))<=MAX_LINELENGTH;s++){
  2027.             if(isprint(*s) && *s!='"' && *s!='\\')
  2028.                 *lineLengthPtr+=fprintf(stream,"%c",(PRINTF_INT)*s);
  2029.             else switch(*s){
  2030.             case '"': *lineLengthPtr+=fprintf(stream,"\\\""); break;
  2031.             case '\\': *lineLengthPtr+=fprintf(stream,"\\\\"); break;
  2032.             case '\n': *lineLengthPtr+=fprintf(stream,"\\n"); break;
  2033.             case '\t': *lineLengthPtr+=fprintf(stream,"\\t"); break;
  2034.             case '\b': *lineLengthPtr+=fprintf(stream,"\\b"); break;
  2035.             case '\r': *lineLengthPtr+=fprintf(stream,"\\r"); break;
  2036.             case '\v': *lineLengthPtr+=fprintf(stream,"\\v"); break;
  2037.             case '\f': *lineLengthPtr+=fprintf(stream,"\\f"); break;
  2038.             case '\312': *lineLengthPtr+=fprintf(stream,"\312"); break;    /* option-space */
  2039.             default: *lineLengthPtr+=fprintf(stream,"\\%03lo"
  2040.                 ,(unsigned long)*(unsigned char *)s); break;
  2041.             }
  2042.         }
  2043.         if(*s!=0){
  2044.             fprintf(stream,"\"" NL);
  2045.             *lineLengthPtr=0;
  2046.             *lineLengthPtr+=fprintf(stream,"    \"");
  2047.         }
  2048.     }while(*s!=0);
  2049.     *lineLengthPtr+=fprintf(stream,closeQuote);
  2050. }
  2051.  
  2052. #if 0
  2053.     /*  A longer, Macintosh-savvy, version of this routine, PrintfExit.c, */
  2054.     /*  is part of the VideoToolbox. */
  2055.     int PrintfExit(const char *format,...)
  2056.     {
  2057.         va_list args;
  2058.         int i;
  2059.       
  2060.         va_start(args,format);
  2061.         i=vfprintf(stdout,format,args);
  2062.         va_end(args);
  2063.         exit(1);
  2064.     }
  2065. #endif
  2066.  
  2067. #if MATLAB
  2068.     int myfprintf(FILE *stream,const char *format,...)
  2069.     {
  2070.         va_list args;
  2071.         int i;
  2072.       
  2073.         va_start(args,format);
  2074.         #if !MATLAB
  2075.             i=vfprintf(stream,format,args);
  2076.         #else
  2077.         {
  2078.             char s[256];
  2079.             stream;    // make compiler happy
  2080.             i=vsprintf(s,format,args);
  2081.             printf("%s",s);
  2082.         }
  2083.         #endif
  2084.         va_end(args);
  2085.         return 0;
  2086.     }
  2087. #endif
  2088.